﻿using LS.Screening.UI.Common;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;

namespace LS.DicomViewer.Common
{
    internal static class DicomViewerHelper
    {
        public static readonly double defaultFrameTime = 83.3333333333D;
        public static readonly double defaultPixelSpacing = 0.046D;
        public static readonly string dicomImageCacheDir = AppHelper.GetFolderByParent(AppHelper.TempDirectory, "Dicom", "ImageCache");
        public static readonly string dicomDicomCacheDir = AppHelper.GetFolderByParent(AppHelper.TempDirectory, "Dicom", "DicomCache");

        /// <summary>
        /// 将文件保存到指定目录
        /// </summary>
        /// <param name="filename">文件路径</param>
        /// <param name="frameTime">帧率</param>
        /// <param name="pixelSpacing">像素间距</param>
        /// <returns></returns>
        public static async Task<DicomInfo> SaveToCache(string filename, double? frameTime, double? pixelSpacing)
        {
            if (filename.EndsWith(".zip"))
                return ZipSaveToCache(filename, frameTime, pixelSpacing);
            else
                return await DicomSaveToCache(filename, frameTime, pixelSpacing);
        }

        /// <summary>
        /// 将dicom文件保存到指定目录
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="frameTime"></param>
        /// <param name="pixelSpacing"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        static async Task<DicomInfo> DicomSaveToCache(string filename, double? frameTime, double? pixelSpacing)
        {
            var file = await DicomFile.OpenAsync(filename);
            if (file == null)
            {
                throw new Exception("加载文件异常，请检查文件是否正确！");
            }
            var dicomInfo = new DicomInfo();
            if (file.Dataset.Contains(DicomTag.PixelData))
            {
                if (file.Dataset.Contains(DicomTag.SequenceOfUltrasoundRegions))
                {
                    var ds = file.Dataset.GetSequence(DicomTag.SequenceOfUltrasoundRegions);
                    for (int i = 0; i < ds.Items.Count; ++i)
                    {
                        DicomDataset dds = ds.Items[i];
                        if (dds.Contains(DicomTag.PhysicalDeltaX))
                        {
                            var pixRate = dds.GetSingleValue<double>(DicomTag.PhysicalDeltaX);
                            if (pixRate != 0)
                            {
                                pixRate = 1 / pixRate;
                                DrawTools.Utils.Dpi.PixelRate = pixRate;
                                dicomInfo.PixelRate = pixRate;
                            }
                            break;
                        }
                    }
                }
                var dataSet = file.Dataset;
                var dicomImage = new DicomImage(dataSet);
                dicomImage.UseVOILUT = false;
                GC.Collect();
                dicomInfo.IsEncapsulated = file.Dataset.InternalTransferSyntax.IsEncapsulated;
                dicomInfo.DicomFile = file;
                dicomInfo.DicomImage = dicomImage;
                dicomInfo.InstanceUid = dataSet.GetSingleValue<string>(DicomTag.SOPInstanceUID);

                file.Dataset.TryGetSingleValue<int>(DicomTag.NumberOfFrames, out int framesCount);

                dicomInfo.NumberOfFrames = framesCount > 0 ? framesCount : 1;

                if (dataSet.Contains(DicomTag.RecommendedDisplayFrameRate))
                {
                    var frameRate = dataSet.GetSingleValue<int>(DicomTag.RecommendedDisplayFrameRate);
                    dicomInfo.DisplayFrameRate = frameRate;
                }
                else if (dataSet.Contains(DicomTag.FrameTime))
                {
                    frameTime = dataSet.GetSingleValue<float>(DicomTag.FrameTime);
                    dicomInfo.DisplayFrameRate = (int)(1000 / frameTime);
                }

                if (file.Dataset.InternalTransferSyntax.IsEncapsulated)//检查是否压缩
                {
                    dicomInfo.ImageFilesDir = EncapsulateSaveToCache(dicomInfo);
                }
            }
            return dicomInfo;
        }

        /// <summary>
        /// 压缩包解压
        /// </summary>
        /// <param name="zipFile"></param>
        /// <param name="frameTime"></param>
        /// <param name="pixelSpacing"></param>
        /// <returns></returns>
        static DicomInfo ZipSaveToCache(string zipFile, double? frameTime, double? pixelSpacing)
        {
            DicomInfo dicomInfo = new DicomInfo();
            var fileName = Path.GetFileNameWithoutExtension(zipFile);
            var fileDir = Path.Combine(dicomImageCacheDir, fileName);
            if (!Directory.Exists(fileDir))
            {
                Directory.CreateDirectory(fileDir);
            }
            if (Directory.GetFiles(fileDir).Length == 0)
            {
                ZipFile.ExtractToDirectory(zipFile, fileDir);
            }
            var pixRate = 10 / (pixelSpacing ?? defaultPixelSpacing);
            DrawTools.Utils.Dpi.PixelRate = pixRate;
            dicomInfo.PixelRate = pixRate;
            dicomInfo.ImageFilesDir = fileDir;
            dicomInfo.NumberOfFrames = Directory.GetFiles(fileDir).Length;
            dicomInfo.DisplayFrameRate = (int)(1000 / (frameTime ?? defaultFrameTime));
            return dicomInfo;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dicomInfo"></param>
        /// <returns></returns>
        static string EncapsulateSaveToCache(DicomInfo dicomInfo)
        {
            var id = dicomInfo.DicomFile.Dataset.GetSingleValue<string>(DicomTag.SOPInstanceUID);
            var fileDir = Path.Combine(dicomImageCacheDir, id);
            if (!Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);
            var pixelData = DicomPixelData.Create(dicomInfo.DicomFile.Dataset);
            dicomInfo.NumberOfFrames = pixelData.NumberOfFrames;
            Parallel.For(0, dicomInfo.NumberOfFrames, i =>
            {
                var tempFile = Path.Combine(fileDir, i + DicomInfo.ImgFilePix);
                if (!File.Exists(tempFile))
                {
                    using (var pinnedByteArray = new PinnedByteArray(pixelData.GetFrame(i).Data))
                    {
                        var data = pinnedByteArray.Data;
                        using (var fs = new FileStream(tempFile, FileMode.OpenOrCreate))
                        {
                            if (dicomInfo.DicomFile.Dataset.InternalTransferSyntax.IsLossy)
                            {
                                using (var img = Image.FromStream(new MemoryStream(data), true, true))
                                {
                                    img.Save(fs, ImageFormat.Jpeg);
                                }
                            }
                            else
                            {
                                fs.Write(data);
                            }
                            fs.Flush();
                        }
                    }
                }
            });
            return fileDir;
        }
    }
}
